/**
 * Universidad Del Valle De Guatemala.
 * Algoritmos y Estructuras de Datos.
 * Sección 20.
 * Miguel Enrique Zea Arenales.
 * Carné 09238.
 * 20/09/2012.
 */
package heapshuffman;

import java.util.ArrayList;

/**
 * @author Miguel Zea
 * Aplicación que permite la codificación de un mensaje mediante el algoritmo de
 * Huffman. Presenta la codificación en un área de texto además que descifra un
 * código empleando la codificación generada.
 */
public class HeapsHuffman extends javax.swing.JFrame {
    String Message, CodeMessage; // Strings de mensajes (codificados o no).
    ArrayList<HuffmanNode> freq = new ArrayList<HuffmanNode>(); // Lista de frecuencia de caracteres.
    ArrayList<HuffmanCode> rescodes = new ArrayList<HuffmanCode>(); // Lista de códigos Huffman resultantes.
    
    /**
     * Creates new form HeapsHuffman
     */
    public HeapsHuffman() {
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jTextField1 = new javax.swing.JTextField();
        jButton1 = new javax.swing.JButton();
        jButton2 = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextArea1 = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jButton1.setText("Generar Codificación");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jButton2.setText("Interpretar Código");
        jButton2.setEnabled(false);
        jButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });

        jTextArea1.setColumns(20);
        jTextArea1.setRows(5);
        jScrollPane1.setViewportView(jTextArea1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                        .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 393, Short.MAX_VALUE)
                        .addComponent(jTextField1))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jButton1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 131, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 138, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jButton1)
                    .addComponent(jButton2))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    /**
     * @pre El campo de texto debe tener algún contenido.
     * @post Se muestra la codificación del mensaje ingresado en el área de texto.
     */
    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
        freq = new ArrayList<HuffmanNode>();
        rescodes = new ArrayList<HuffmanCode>();
        Message = jTextField1.getText(); // Se obtiene el mensaje a codificar.
        if(!jTextField1.getText().isEmpty()) {
            jTextArea1.setText(genEncoding(Message, freq, rescodes)); // Se realiza la codificación.
            jButton2.setEnabled(true); // Se activa el botón para descifrar mensajes.
        }
    }//GEN-LAST:event_jButton1ActionPerformed

    /**
     * @pre El campo de texto debe tener algún contenido (codificado), debe haberse presionado el botón 1.
     * @post Se muestra el mensaje resultado de aplicar la codificación al código en el área de texto.
     */
    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
        CodeMessage = jTextField1.getText(); // Se obtiene el mensaje codificado.
        jTextArea1.append("Mensaje Resultante:\n" + genMessage(CodeMessage, rescodes) + "\n"); // Se despliega el mensaje correspondiente.
    }//GEN-LAST:event_jButton2ActionPerformed

    /**
     * @pre Debe haberse presionado el botón 1.
     * @post Regresa la codificación del mensaje ingresado luego de emplear el algoritmo de Huffman.
     */
    public String genEncoding(String InputMessage, ArrayList<HuffmanNode> Frequency, ArrayList<HuffmanCode> Codes) {
        char c;
        
        for(int i = 0; i < InputMessage.length(); i++) {
            c = InputMessage.charAt(i);
            if (c == '\n') continue;
            HuffmanNode query = new HuffmanNode(c);
            if (!Frequency.contains(query)) { // Se busca el caracter en la lista de frecuencias.
                Frequency.add(query); // Si no existe, se agrega.
            } else { // Si se encuentra, se incrementa su frecuencia.
                HuffmanNode item = Frequency.remove(Frequency.indexOf(query));
                item.frequency++;
                Frequency.add(item);
            }
        }
  
        // Se inserta cada caracter en un árbol de Huffman.
        PriorityQueue<HuffmanTree> trees = new PriorityQueueSkewHeap<HuffmanTree>();
        for (HuffmanNode n : Frequency) {
            trees.add(new HuffmanTree(n));
        }

        // Se combinan los árboles de Huffman en parejas hasta que sólo reste 1.
        while (trees.size() > 1) {
            // Se toman los 2 valores menores.
            HuffmanTree smallest = (HuffmanTree)trees.remove();
            HuffmanTree small = (HuffmanTree)trees.remove();
            // Se crea un nuevo árbol que contenga a ambos y se agrega a la lista.
            trees.add(new HuffmanTree(smallest,small));
        }
        HuffmanTree encoding = trees.remove();
        encoding.print(Codes);
        return EncodingFormat(Codes);
    }
    
    /**
     * @pre La lista Codes contiene las parejas de caracteres y códigos resultado de la codificación.
     * @post Regresa un detalle de la codificación del mensaje.
     */
    private String EncodingFormat(ArrayList<HuffmanCode> Codes) {
        String formatStr = "";
        formatStr = formatStr + "Codificación:\n------------------\n"; 
        for(int i = 0; i < Codes.size(); i++) { // Se despliegan todas las parejes de caracter y código.
            formatStr = formatStr + Codes.get(i).ch + "\t-->\t" + Codes.get(i).code + "\n"; 
        }
        return formatStr;
    }
    
    /**
     * @pre Debe haberse presionado el botón 2.
     * @post Regresa una cadena con el código decodificado empleando los resultados del algoritmo de Huffman.
     */
    public String genMessage(String InputCode, ArrayList<HuffmanCode> Codes) {
        String compStr = "", Result = "";
        
        for(int h = 0; h < InputCode.length(); h++) {
            compStr = compStr + InputCode.charAt(h); // Se forma una subcadena agregando cada elemento de la cadena codificada. 
            for(int k = 0; k < Codes.size(); k++) {
                if(compStr.equals(Codes.get(k).code)) { // Se evalúa si la subcadena coincide con algún código.
                    Result = Result + Codes.get(k).ch; // Si lo hace, se agrega el caracter correspondiente  
                    compStr = ""; // a la cadena resultado y se limpia la subcadena.
                }
            }
        }
        return Result;
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /*
         * Set the Nimbus look and feel
         */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /*
         * If Nimbus (introduced in Java SE 6) is not available, stay with the
         * default look and feel. For details see
         * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(HeapsHuffman.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(HeapsHuffman.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(HeapsHuffman.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(HeapsHuffman.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /*
         * Create and display the form
         */
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new HeapsHuffman().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextArea jTextArea1;
    private javax.swing.JTextField jTextField1;
    // End of variables declaration//GEN-END:variables
}
